/* * Copyright 2004,2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.bsf.engines.xslt; import java.io.File; import java.io.Reader; import java.io.StringReader; import java.net.URL; import java.util.Vector; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamSource; import org.apache.bsf.BSFDeclaredBean; import org.apache.bsf.BSFException; import org.apache.bsf.BSFManager; import org.apache.bsf.util.BSFEngineImpl; import org.apache.bsf.util.BSFFunctions; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.xpath.objects.XObject; import org.w3c.dom.Node; /** * Xerces XSLT interface to BSF. Requires Xalan and Xerces from Apache. * * This integration uses the BSF registry to pass in any src document * and stylesheet base URI that the user may wish to set. * * @author Sanjiva Weerawarana * @author Sam Ruby * * Re-implemented for the Xalan 2 codebase * * @author Victor J. Orlikowski */ public class XSLTEngine extends BSFEngineImpl { TransformerFactory tFactory; Transformer transformer; Log logger = LogFactory.getLog(this.getClass().getName()); /** * call the named method of the given object. */ public Object call (Object object, String method, Object[] args) throws BSFException { throw new BSFException (BSFException.REASON_UNSUPPORTED_FEATURE, "BSF:XSLTEngine can't call methods"); } /** * Declare a bean by setting it as a parameter */ public void declareBean (BSFDeclaredBean bean) throws BSFException { transformer.setParameter (bean.name, new XObject (bean.bean)); } /** * Evaluate an expression. In this case, an expression is assumed * to be a stylesheet of the template style (see the XSLT spec). */ public Object eval (String source, int lineNo, int columnNo, Object oscript) throws BSFException { // get the style base URI (the place from where Xerces XSLT will // look for imported/included files and referenced docs): if a // bean named "xslt:styleBaseURI" is registered, then cvt it // to a string and use that. Otherwise use ".", which means the // base is the directory where the process is running from Object sbObj = mgr.lookupBean ("xslt:styleBaseURI"); String styleBaseURI = (sbObj == null) ? "." : sbObj.toString (); // Locate the stylesheet. StreamSource styleSource; styleSource = new StreamSource(new StringReader(oscript.toString ())); styleSource.setSystemId(styleBaseURI); try { transformer = tFactory.newTransformer(styleSource); } catch (Exception e) { logger.error("Exception from Xerces XSLT:", e); throw new BSFException (BSFException.REASON_EXECUTION_ERROR, "Exception from Xerces XSLT: " + e, e); } // get the src to work on: if a bean named "xslt:src" is registered // and its a Node, then use it as the source. If its not a Node, then // if its a URL parse it, if not treat it as a file and make a URL and // parse it and go. If no xslt:src is found, use an empty document // (stylesheet is treated as a literal result element stylesheet) Object srcObj = mgr.lookupBean ("xslt:src"); Object xis = null; if (srcObj != null) { if (srcObj instanceof Node) { xis = new DOMSource((Node)srcObj); } else { try { String mesg = "as anything"; if (srcObj instanceof Reader) { xis = new StreamSource ((Reader) srcObj); mesg = "as a Reader"; } else if (srcObj instanceof File) { xis = new StreamSource ((File) srcObj); mesg = "as a file"; } else { String srcObjstr=srcObj.toString(); xis = new StreamSource (new StringReader(srcObjstr)); if (srcObj instanceof URL) { mesg = "as a URL"; } else { ((StreamSource) xis).setPublicId (srcObjstr); mesg = "as an XML string"; } } if (xis == null) { throw new Exception ("Unable to get input from '" + srcObj + "' " + mesg); } } catch (Exception e) { throw new BSFException (BSFException.REASON_EXECUTION_ERROR, "BSF:XSLTEngine: unable to get " + "input from '" + srcObj + "' as XML", e); } } } else { // create an empty document - real src must come into the // stylesheet using "doc(...)" [see XSLT spec] or the stylesheet // must be of literal result element type xis = new StreamSource(); } // set all declared beans as parameters. for (int i = 0; i < declaredBeans.size (); i++) { BSFDeclaredBean b = (BSFDeclaredBean) declaredBeans.elementAt (i); transformer.setParameter (b.name, new XObject (b.bean)); } // declare a "bsf" parameter which is the BSF handle so that // the script can do BSF stuff if it wants to transformer.setParameter ("bsf", new XObject (new BSFFunctions (mgr, this))); // do it try { DOMResult result = new DOMResult(); transformer.transform ((StreamSource) xis, result); return new XSLTResultNode (result.getNode()); } catch (Exception e) { throw new BSFException (BSFException.REASON_EXECUTION_ERROR, "exception while eval'ing XSLT script" + e, e); } } /** * Initialize the engine. */ public void initialize (BSFManager mgr, String lang, Vector declaredBeans) throws BSFException { super.initialize (mgr, lang, declaredBeans); tFactory = TransformerFactory.newInstance(); } /** * Undeclare a bean by setting he parameter represeting it to null */ public void undeclareBean (BSFDeclaredBean bean) throws BSFException { // Cannot clear only one parameter in Xalan 2, so we set it to null if ((transformer.getParameter (bean.name)) != null) { transformer.setParameter (bean.name, null); } } }